package jehc.cloud.iot.live.common.util;

import jehc.cloud.common.util.StringUtil;
import jehc.cloud.iot.live.model.CameraEntity;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
/**
 * @Desc 视频录制类
 * @Author 邓纯杰
 * @CreateTime 2012-12-12 12:12:12
 */
@Slf4j
public class TranscribeUtil {
    protected FFmpegFrameGrabber grabber = null;// 解码器
    protected FFmpegFrameRecorder record = null;// 编码器
    private CameraEntity cameraEntity;
    private double frameRate = 5;
    private long startTime = 0;//录制开始时间
    private long videoTS = 0;//最后一次时间
    private long pauseTime = 0;//暂停时间
    public TranscribeUtil(){
        super();
    }
    public TranscribeUtil(CameraEntity cameraEntity){
        this.cameraEntity = cameraEntity;
    }

    public TranscribeUtil(CameraEntity cameraEntity,Thread nowThread){
        this.cameraEntity = cameraEntity;
    }

    /**
     * 按帧录制视频
     * description: 如果获取到的视频流有卡顿? 则要分析视频源，如果视频源本身没有卡顿的话，可以结合视频源的刷新率，考虑提高grabber的帧率，同时提高recorder的帧率。如果视频源本身就有卡顿话，那就需要另外想办法解决视频源卡顿的问题。
     */
    public TranscribeUtil startFrameRecord(){
        try {
            if(StringUtil.isEmpty(cameraEntity.getVideoName())){
                log.info("未能获取到录制输出视频名称！");
                return null;
            }
            if(StringUtil.isEmpty(cameraEntity.getVideoType())){
                log.info("未能获取到录制输出视频类型！");
                return null;
            }
            String inputFile = cameraEntity.getRtspUrl(); //-该地址可以是网络直播/录播地址，也可以是远程/本地文件路径
            String outputFile = cameraEntity.getPath()+"/"+cameraEntity.getVideoName()+"."+cameraEntity.getVideoType();// -该地址只能是文件地址，如果使用该方法推送流媒体服务器会报错，原因是没有设置编码格式
            log.info("录屏输出路径："+outputFile);
            Integer audioChannel = cameraEntity.getAudio();//是否录制音频0:不录制/1:录制
            if(StringUtil.isEmpty(inputFile)){
                log.info("未能获取到视频源地址！");
                return null;
            }
            if(StringUtil.isEmpty(outputFile)){
                log.info("未能获取到录制输出地址！");
                return null;
            }
            if(null == audioChannel){
                log.info("未能获取到录制音频参数！");
                return null;
            }
            boolean isStart = true;//该变量建议设置为全局控制变量，用于控制录制结束
            // 获取视频源
            grabber = new FFmpegFrameGrabber(inputFile);//设置视频源地址
            // 获取流设置帧率参数
            grabber.setOption("rtsp_transport", "tcp");
            grabber.setFrameRate(30);
            grabber.setVideoBitrate(3000000);
            // 流媒体输出地址，分辨率（长，高），是否录制音频（0:不录制/1:录制）
            record = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);

            /**
             * 权衡quality(视频质量)和encode speed(编码速度) values(值)： ultrafast(终极快),superfast(超级快),
             * veryfast(非常快), faster(很快), fast(快), medium(中等), slow(慢), slower(很慢),
             * veryslow(非常慢)
             * ultrafast(终极快)提供最少的压缩（低编码器CPU）和最大的视频流大小；而veryslow(非常慢)提供最佳的压缩（高编码器CPU）的同时降低视频流的大小
             * 参考：https://trac.ffmpeg.org/wiki/Encode/H.264 官方原文参考：-preset ultrafast as the
             * name implies provides for the fastest possible encoding. If some tradeoff
             * between quality and encode speed, go for the speed. This might be needed if
             * you are going to be transcoding multiple streams on one machine.
             */
            record.setVideoOption("preset", "slow");

            //record.setFormat("mov,mp4,m4a,3gp,3g2,mj2,h264,ogg,MPEG4");
            record.setSampleRate(44100);
            record.setFrameRate(frameRate);
            record.setVideoQuality(0);
            record.setVideoOption("crf", "23");

            // 输出流设置帧率参数
            record.setFrameRate(30);
            record.setVideoBitrate(3000000);// 开始取视频源,2000 kb/s, 720P视频的合理比特率范围
            record.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); // yuv420p
        }catch (Exception e){
            log.error("录制出现异常：{}",e);
        }
        return this;
    }

    /**
     *
     *保存视频源到本地
     * @throws Exception
     * @throws org.bytedeco.javacv.FrameRecorder.Exception
     */
    public TranscribeUtil doStartFrameRecord(Thread nowThread){
        try {
            // 用来标记控制
            //建议在线程中使用该方法
            grabber.start();
            record.start();
            Frame frame = null;
            while (cameraEntity.getErrorCount() < 101 && cameraEntity.getControlTranscribe() && (frame = grabber.grabFrame()) != null) {//错误次数不能超过100，终止状态为true，并且能获取到帧
                record.record(frame);
                /*
                videoTS = 1000L* (System.currentTimeMillis() - startTime - (System.currentTimeMillis() - pauseTime));
                record.setTimestamp(videoTS );
                */
            }
            release(grabber,record);
        }catch (Exception e){
            if(e.getMessage().equals("[swscaler @ 0000000025dca580] deprecated pixel format used, make sure you did set range correctly")){
                log.warn("警告，格式已被弃用");
            }else{
                cameraEntity.setErrorCount(cameraEntity.getErrorCount()+1);
            }
        }finally {
            release(grabber,record);
        }
        return this;
    }

    /**
     * 释放
     * @param grabber
     * @param record
     * @throws org.bytedeco.javacv.FrameGrabber.Exception
     * @throws org.bytedeco.javacv.FrameRecorder.Exception
     */
    public void release(FFmpegFrameGrabber grabber,FFmpegFrameRecorder record){
       try {
           if(null != record){
               record.stop();
               record.close();
           }
           if(null != grabber){
               grabber.stop();
               grabber.close();
           }
       }catch (Exception e){
           log.error("终止record，grabber异常：{}",e);
       }
    }
}
